package cn.fang2chen.java.distributed.lock.framework;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 可缓存的 分布式工厂
 *
 * @author dcmini chen
 * @since 1.0.0
 */
public class CacheAbleDistLockFactory<T extends DistLockCopyAble> implements DistributedLockFactory<T> {

    public static Long NOT_EXPIRED_FOREVER = -1L;

    /**
     * 储存锁对象
     */
    private final ConcurrentHashMap<String, T> lockMap = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, Long> deadTimeMap = new ConcurrentHashMap<>();

    private long expiredTime = NOT_EXPIRED_FOREVER;
    private final DistLockCopyAble template;

    public CacheAbleDistLockFactory(final DistLockCopyAble template) {
        this.template = template;
    }

    public CacheAbleDistLockFactory(final long expiredTime, final DistLockCopyAble template) {
        this.expiredTime = expiredTime;
        this.template = template;
    }

    @Override
    public T create(RemoteLockParam remoteLockParam) {
        synchronized (this) {
            clearExpiredLock();
            T distributedLock = (T) template.copy(remoteLockParam);
            addNew(distributedLock, remoteLockParam);
            return distributedLock;
        }

    }

    private void addNew(T distributedLock, RemoteLockParam remoteLockParam) {
        String lockPath = remoteLockParam.getLockPath();
        lockMap.put(lockPath, distributedLock);
        if (expiredTime == NOT_EXPIRED_FOREVER) {
            deadTimeMap.put(lockPath, NOT_EXPIRED_FOREVER);
        } else {
            deadTimeMap.put(lockPath, System.currentTimeMillis() + expiredTime);
        }
    }

    private void clearExpiredLock() {
        if (expiredTime == NOT_EXPIRED_FOREVER) {
            return;
        }

        List<String> needRemoveLock = new ArrayList<>();
        for (Map.Entry<String, Long> stringLongEntry : deadTimeMap.entrySet()) {
            String lockPath = stringLongEntry.getKey();
            Long deadTime = stringLongEntry.getValue();
            if (System.currentTimeMillis() >= deadTime) {
                needRemoveLock.add(lockPath);
            }
        }

        for (String lockKey : needRemoveLock) {
            lockMap.remove(lockKey);
            deadTimeMap.remove(lockKey);
        }
    }

    @Override
    public T tryGet(String lockPath) {
        synchronized (this) {
            clearExpiredLock();
            return lockMap.get(lockPath);
        }

    }
}
